package org.myframework.rest.commons;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;


import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.condition.ConsumesRequestCondition;
import org.springframework.web.servlet.mvc.condition.HeadersRequestCondition;
import org.springframework.web.servlet.mvc.condition.ParamsRequestCondition;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.ProducesRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.view.UrlBasedViewResolver;

/**
 * 扫描@RestService注解类，并发布成rest服务， 默认服务名为:/<类名>/<方法名>,默认HTTP请求方式为GET或POST
 * <ol>
 * <li>{@link RestService }</li>
 * </ol>
 *
 * @author wanghui
 * @since 1.0
 */
public class RestControllerHandlerMapping extends RequestMappingHandlerMapping {

	private static final String REDIRECT = UrlBasedViewResolver.REDIRECT_URL_PREFIX;

	private static final String FORWARD = UrlBasedViewResolver.FORWARD_URL_PREFIX;

	private Map<String, String> mappings = new HashMap<String, String>();

	private Set<String> paths = new HashSet<String>();

	public RestControllerHandlerMapping() {
		setOrder(2);
	}

	/**
	 * Detects &#64;RestService annotations in handler beans.
	 *
	 * @see org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#isHandler(java.lang.Class)
	 */
	@Override
	protected boolean isHandler(Class<?> beanType) {
		return AnnotationUtils.findAnnotation(beanType, RestController.class) != null;
	}



	@Override
	protected RequestMappingInfo getMappingForMethod(Method method,
			Class<?> handlerType) {

		// public方法才能发布rest服务
		if (!isPublicMethod(method)) {
			logger.info("method is not public , can not register rest service .."
					+ method.toGenericString());
			return null;
		}
		logger.info("register rest service .." + method.toGenericString());
		RequestMappingInfo clzInfo = null;
		RequestMappingInfo methodInfo = null;
		RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(
				method, RequestMapping.class);
		RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(
				handlerType, RequestMapping.class);
		RequestCondition<?> methodCondition = getCustomMethodCondition(method);
		RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);

		if (methodAnnotation != null) {
			methodInfo = createRequestMappingInfo(methodAnnotation,
					methodCondition);
		} else {
			methodInfo = createMethodRequestMappingInfo(method, methodCondition);
		}

		if (typeAnnotation != null) {
			clzInfo = createRequestMappingInfo(typeAnnotation, typeCondition);
		} else {
			clzInfo = createClassRequestMappingInfo(handlerType, typeCondition);
		}

		return clzInfo.combine(methodInfo);

	}

	/**
	 * 根据类名构造RequestMappingInfo
	 *
	 * @see RequestMappingInfo
	 * @param handlerType
	 * @param customCondition
	 * @return
	 */
	protected RequestMappingInfo createClassRequestMappingInfo(
			Class<?> handlerType, RequestCondition<?> customCondition) {
		String[] paths = { handlerType.getName(), handlerType.getSimpleName(),
				StringUtils.uncapitalize(handlerType.getSimpleName()) };
		return new RequestMappingInfo(handlerType.getName(),
				new PatternsRequestCondition(paths, getUrlPathHelper(),
						getPathMatcher(), useSuffixPatternMatch(),
						useTrailingSlashMatch(), getFileExtensions()),
				new RequestMethodsRequestCondition(),
				new ParamsRequestCondition(new String[] {}),
				new HeadersRequestCondition(new String[] {}),
				new ConsumesRequestCondition(new String[] {}, new String[] {}),
				new ProducesRequestCondition(new String[] {}, new String[] {},
						getContentNegotiationManager()), customCondition);
	}

	/**
	 * 根据方法名构造RequestMappingInfo
	 *
	 * @see RequestMappingInfo
	 * @param method
	 * @param customCondition
	 * @return
	 */
	protected RequestMappingInfo createMethodRequestMappingInfo(Method method,
			RequestCondition<?> customCondition) {
		return new RequestMappingInfo(method.getName(),
				new PatternsRequestCondition(new String[] { method.getName() },
						getUrlPathHelper(), getPathMatcher(),
						useSuffixPatternMatch(), useTrailingSlashMatch(),
						getFileExtensions()),
				new RequestMethodsRequestCondition(RequestMethod.POST,
						RequestMethod.GET), new ParamsRequestCondition(
						new String[] {}), new HeadersRequestCondition(
						new String[] {}), new ConsumesRequestCondition(
						new String[] {}, new String[] {}),
				new ProducesRequestCondition(new String[] {}, new String[] {},
						getContentNegotiationManager()), customCondition);
	}

	private static final int PUBLIC = 0x00000001;

	private boolean isPublicMethod(Method method) {
		int mod = method.getModifiers();
		return (mod & PUBLIC) != 0;
	}

}
